// HomeNet Devices - Simple
// 12-13-2010 <mdoll@email.usf.edu> http://opensource.org/licenses/mit-license.php

#include "HomeNet.h"
#include "HomeNetDevices.h"
#include <WProgram.h>

//Arduino///////////////////////////////////////////////////////////////////////

HomeNetPartialPacket* HomeNetDeviceArduino::process(const uint16_t& fromNode, const uint8_t& fromDevice, const uint8_t& command, const HomeNetPayload& payload) {
#ifdef DEBUG
    Serial.print("Arduino Processing: ");
    Serial.println(command, DEC);
#endif

    switch (command) {
        case CMD_ERROR:
            //addUdpPacket(0, 0, 0, CMD_PONG, getPayload(packet));
            break;
        case CMD_VERSION:
            return partialPacket(_location, CMD_BYTE, HomeNetPayload(_homeNet->getVersion()));
            break;
        case CMD_MEMORYFREE:
            return partialPacket(_location, CMD_INT, HomeNetPayload(_homeNet->availableMemory()));
            break;
        case CMD_PING:
            return partialPacket(_location, CMD_PONG, payload);
            break;
        case CMD_ACK:
            //return partialPacket(_location, CMD_PONG, payload);
            break;
        case CMD_GETNODEID:
            //return partialPacket(_location, CMD_PONG, payload);
            break;
        case CMD_SETNODEID:
            //return partialPacket(_location, CMD_PONG, payload);
            break;
        case CMD_GETDEVICE:
            //return partialPacket(_location, CMD_PONG, payload);
            break;
        case CMD_SETDEVICE:
            //return partialPacket(_location, CMD_PONG, payload);
            break;
    }
    return NULL;
}

//JeeNode///////////////////////////////////////////////////////////////////////

HomeNetPartialPacket* HomeNetDeviceJeeNode::process(const uint16_t& fromNode, const uint8_t& fromDevice, const uint8_t& command, const HomeNetPayload& payload) {
#ifdef DEBUG
    Serial.print("JeeNode Processing: ");
    Serial.println(command, DEC);
#endif

    HomeNetPartialPacket* partial = HomeNetDeviceArduino::process(fromNode, fromDevice, command, payload);
    if(partial != NULL){
        return partial;
    }

    switch (command) {
        case CMD_BATTERYLEVEL:
            //return partialPacket(_location, CMD_PONG, payload);
            break;
    }
    return NULL;
}

////////////////////////////TMP37////////////////////////////

HomeNetPartialPacket* HomeNetDeviceTMP37::process(const uint8_t& command, const HomeNetPayload& payload)
{
    switch(command){

        case CMD_GETVALUE:
            return partialPacket(_location, CMD_FLOAT, HomeNetPayload(readSensor()));
            break;
    }
    return NULL;
}

float HomeNetDeviceTMP37::readSensor() {
    _port->mode2(INPUT); // set pin to input
    float value = _port->anaRead();
    //Based on code from Modern Device: http://shop.moderndevice.com/products/temperature-sensor
#if TEMPERATURE_FORMAT == 0
    float celsius = (value * 3.3 / 1024.0) / .02; //celsius
    //uint16_t farenheit = (((value * 5.0 / 1024.0) / .02) * 9.0 / 5.0) + 32; //farenheit

    return celsius;
#else
    float farenheit = (((value * 3.3 / 1024.0) / .02) * 9.0 / 5.0) + 32; //farenheit

    return farenheit;
#endif

}

////////////////////////////TMP421////////////////////////////

void HomeNetDeviceTMP421::init(uint8_t location) {
#ifdef DEBUG
    Serial.println("Init TMP421");
#endif
    HomeNetDevicePortI2C::init(location);
    _i2c = new DeviceI2C(*_port, 0x2A);
}

HomeNetPartialPacket* HomeNetDeviceTMP421::process(const uint8_t& command, const HomeNetPayload& payload) {
    switch (command) {

        case CMD_GETVALUE:
            return partialPacket(_location, CMD_FLOAT, HomeNetPayload(readSensor()));
            break;
    }
    return NULL;
}

float HomeNetDeviceTMP421::readSensor() {

    int8_t tempC_hi = 0;
    uint8_t tempC_lo = 0;
    float tempC = 0;

    //  float value = _port->anaRead();
    //Based on code from Modern Device: http://shop.moderndevice.com/products/temperature-sensor

    _i2c->send();
    _i2c->write(0x00); // high byte is on register 0
    _i2c->receive();
    tempC_hi = _i2c->read(1);
    _i2c->stop();

    _i2c->send();
    _i2c->write(0x10); // low byte is on register 0x10
    _i2c->receive();
    tempC_lo = _i2c->read(1);
    _i2c->stop();

    tempC = (float) tempC_lo / 256; // make low byte into remainder
    tempC = tempC + tempC_hi;

#if TEMPERATURE_FORMAT == 0
    // celsius
    return tempC;
#else
    //farenheit
    return ((tempC) * 9.0 / 5.0) + 32;
#endif
}

//LDR///////////////////////////////////////////////////////////////////////////

HomeNetPartialPacket* HomeNetDeviceLDR::process(const uint8_t& command, const HomeNetPayload& payload)
{
    switch(command){

        case CMD_GETVALUE:
            return partialPacket(_location, CMD_INT, HomeNetPayload(readSensor()));
            break;
    }
    return NULL;
}

uint16_t HomeNetDeviceLDR::readSensor(){
   _port->mode2(INPUT);           // set pin to input
   _port->digiWrite2(HIGH);
   uint16_t value = map(_port->anaRead(),0,1023,100,0);
   _port->digiWrite2(LOW);
   return value;
}

//SWITCH////////////////////////////////////////////////////////////////////////

void HomeNetDeviceSwitch::init(uint8_t location) {
#ifdef DEBUG
    Serial.println("Switch Initialized");
#endif
    HomeNetDevicePort::init(location);
    
    //attach interrupt
   // attachInterrupt(1, onInterrupt, CHANGE);

    _port->mode3(INPUT);
    _port->digiWrite3(HIGH);//turn on internal pullup
}

HomeNetPartialPacket* HomeNetDeviceSwitch::process(const uint8_t& command, const HomeNetPayload& payload)
{
    if(_port->digiRead3() == HIGH){
        _state = 0;
    } else {
        _state = 255;
    }

    switch(command){

        case CMD_GETVALUE:
            //Serial.println(_port->digiRead(),DEC);
            return partialPacket(_location, CMD_BYTE, HomeNetPayload(_state));
            break;
    }
    return NULL;
}
/*
void  HomeNetDeviceSwitch::updateOften(){
    if(HomeNetDeviceSwitch::deviceInterrupt > 0){
        HomeNetDeviceSwitch::deviceInterrupt = 0;
        _homeNet->deviceInterrupt();
     }
}
*/
//CONTACT SWITCH////////////////////////////////////////////////////////////////

void HomeNetDeviceContactSwitch::init(uint8_t location) {
#ifdef DEBUG
    Serial.println("Contact Switch Initialized");
#endif
    HomeNetDevicePort::init(location);
    attachInterrupt(1, onInterrupt, CHANGE); //Setup interrupt
    //turn on internal pullup
    //_port->mode(INPUT);
    //_port->digiWrite(HIGH);
    //setup the interrupt the same
    _port->mode3(INPUT);
    _port->digiWrite3(HIGH);
}

HomeNetPartialPacket* HomeNetDeviceContactSwitch::process(const uint8_t& command, const HomeNetPayload& payload) {
#ifdef DEBUG
    Serial.println("Contact Switch processing");
#endif
    if(_port->digiRead3() == HIGH){
        _state = 255;
    } else {
        _state = 0;
    }

    switch(command){

        case CMD_GETVALUE:
            //Serial.println(_port->digiRead(),DEC);
            return partialPacket(_location, CMD_BYTE, HomeNetPayload(_state));
            break;
    }
    return NULL;
}

//LED///////////////////////////////////////////////////////////////////////////
void HomeNetDeviceLED::init(uint8_t location) {
#ifdef DEBUG
    Serial.println("LED Initialized");
#endif
    HomeNetDevicePort::init(location);

    //fade light up and down on boot
    for(int i = 0; i<=255; i++){
        led(i);
        delay(5);
    }
    delay(10);
    for(int i = 255; i>=0; i--){
        led(i);
        delay(5);
    }
}

HomeNetPartialPacket* HomeNetDeviceLED::process(const uint8_t& command, const HomeNetPayload& payload)
{
#ifdef DEBUG
    Serial.println("LED Processing");
#endif

    switch(command){

        case CMD_GETVALUE:
            return partialPacket(_location, CMD_BYTE, HomeNetPayload(_level));
            break;

         case CMD_BYTE:
         case CMD_LEVEL:
         case CMD_SETVALUE:
            led(payload.data[0]);
            break;

         case CMD_ON:      
             led(255);
            break;

         case CMD_OFF:
            led(0);  
            break;
    }
    return NULL;
}

void HomeNetDeviceLED::led(uint8_t on) {
#ifdef DEBUG
    Serial.println("Light A Called");
#endif
    _level = on;
    _port->mode(OUTPUT);
    _port->anaWrite(_level);
}

//MotionSensor////////////////////////////////////////////////////////////////////////

void HomeNetDeviceMotionSensor::init(uint8_t location) {
#ifdef DEBUG
    Serial.println(_timeout, DEC);
    Serial.println(" Motion Sensor Initialized");
#endif

    HomeNetDevicePort::init(location);

    //Setup interrupt
    //turn on internal pullup
    //_port->mode(INPUT);
    //_port->digiWrite(HIGH);
    //setup the interrupt the same
    _port->mode3(INPUT);
    _port->digiWrite3(HIGH);
    _state = 0;
    _lastState = 0;
    _count = 0;
}

void HomeNetDeviceMotionSensor::update() {
    if (_count > 0) {
        _count--;
    }
    if (_count == 1) {
#ifdef DEBUG
        Serial.println("Motion Sensor timeout");
#endif
       //this is a hack to get the intterupt to trigger 

        _homeNet->deviceInterrupt(true);
    }
}

HomeNetPartialPacket* HomeNetDeviceMotionSensor::interrupt(const uint8_t& command, const HomeNetPayload& payload) {
#ifdef DEBUG
    Serial.println("Motion Sensor Triggered");
#endif
    if (_port->digiRead3() == HIGH) {
        _state = 255;
        _count = _timeout + 1;
    } else {
        _state = 0;
    }
#ifdef DEBUG
    Serial.print(_state, DEC);
    Serial.println(" Motion state");
#endif
    if(_state != _lastState){
        _lastState = _state;
        return process(command, payload);
    }
    return NULL;
}

HomeNetPartialPacket* HomeNetDeviceMotionSensor::process(const uint8_t& command, const HomeNetPayload& payload)
{
    #ifdef DEBUG
    Serial.println("Motion Sensor process");
#endif
    switch(command){

        case CMD_GETVALUE:
            //Serial.println(_port->digiRead(),DEC);
            return partialPacket(_location, CMD_BYTE, HomeNetPayload(_state));
            break;
    }
    Serial.println(command, DEC);
    return NULL;
}


//SMARTOUTLET///////////////////////////////////////////////////////////////////

void HomeNetDeviceSmartOutlet::init(uint8_t location) {
#ifdef DEBUG
    Serial.println("Outlet Initialized");
#endif
    HomeNetDevicePort::init(location);
    
    outlet1(1);
    outlet2(1);
    delay(200);
    outlet1(0);
    outlet2(0);
}


HomeNetPartialPacket* HomeNetDeviceSmartOutlet::process(const uint8_t& command, const HomeNetPayload& payload) {

    switch (command) {

        case CMD_GETVALUE:
            if (payload.length == 0) {

            } else {

            }
            break;
        case CMD_BYTE:
        case CMD_SETVALUE:
            if (payload.length == 2) {
                outlet1(payload.data[0]);
                outlet2(payload.data[1]);
            }
            break;
        case CMD_ON:
            if (payload.length == 1) {
                if (payload.data[0] == 0) {
                    outlet1(1);
                } else {
                    outlet2(1);
                }
            }
            break;
        case CMD_OFF:
            if (payload.length == 1) {
                if (payload.data[0] == 0) {
                    outlet1(0);
                } else {
                    outlet2(0);
                }
            }
            break;
    }
    return NULL;
}
void HomeNetDeviceSmartOutlet::outlet1(uint8_t on)
{
     _state1 = on;
    _port->mode(OUTPUT);
    if (on > 0) {
        _port->digiWrite(1);
        return;
    }
    _port->digiWrite(0);
}
void HomeNetDeviceSmartOutlet::outlet2(uint8_t on)
{
    _state2 = on;
    _port->mode2(OUTPUT);
    if (on > 0) {
        _port->digiWrite2(!1);
        return;
    }
    _port->digiWrite2(!0);
}